เรียนรู้แนวคิดหลักและเทคนิคขั้นสูงของการเรนเดอร์เงาแบบเรียลไทม์ใน WebGL คู่มือนี้ครอบคลุม shadow mapping, PCF, CSM และวิธีแก้ปัญหา artifacts ทั่วไป
WebGL Shadow Mapping: คำแนะนำที่ครอบคลุมเกี่ยวกับการเรนเดอร์แบบเรียลไทม์
ในโลกของกราฟิกคอมพิวเตอร์ 3D มีองค์ประกอบเพียงไม่กี่อย่างที่ช่วยเพิ่มความสมจริงและความดื่มด่ำได้มากกว่าเงา เงาให้สัญญาณภาพที่สำคัญเกี่ยวกับความสัมพันธ์เชิงพื้นที่ระหว่างวัตถุ ตำแหน่งของแหล่งกำเนิดแสง และรูปทรงโดยรวมของฉาก หากไม่มีเงา โลก 3D อาจดูแบน ไม่เชื่อมต่อกัน และไม่สมจริง สำหรับแอปพลิเคชัน 3D บนเว็บที่ขับเคลื่อนโดย WebGL การใช้เงาแบบเรียลไทม์คุณภาพสูงถือเป็นจุดเด่นของประสบการณ์ระดับมืออาชีพ คู่มือนี้จะให้ข้อมูลเชิงลึกเกี่ยวกับเทคนิคพื้นฐานและใช้กันอย่างแพร่หลายที่สุดเพื่อให้บรรลุเป้าหมายนี้: Shadow Mapping
ไม่ว่าคุณจะเป็นโปรแกรมเมอร์กราฟิกที่มีประสบการณ์หรือนักพัฒนาเว็บที่ก้าวเข้าสู่มิติที่สาม บทความนี้จะช่วยให้คุณมีความรู้ความเข้าใจ ใช้งาน และแก้ไขปัญหาเงาแบบเรียลไทม์ในโปรเจ็กต์ WebGL ของคุณ เราจะเดินทางจากทฤษฎีหลักไปสู่รายละเอียดการใช้งานจริง สำรวจข้อผิดพลาดทั่วไปและเทคนิคขั้นสูงที่ใช้ในเอนจินกราฟิกสมัยใหม่
บทที่ 1: พื้นฐานของ Shadow Mapping
หัวใจสำคัญของ shadow mapping คือเทคนิคที่ชาญฉลาดและสง่างามที่พิจารณาว่าจุดในฉากอยู่ในเงาหรือไม่โดยการถามคำถามง่ายๆ: "จุดนี้สามารถมองเห็นได้จากแหล่งกำเนิดแสงหรือไม่" หากคำตอบคือไม่ แสดงว่ามีบางสิ่งกีดขวางแสง และจุดนั้นต้องอยู่ในเงา เพื่อตอบคำถามนี้ด้วยโปรแกรม เราใช้แนวทางการเรนเดอร์สองรอบ
Shadow Mapping คืออะไร? แนวคิดหลัก
เทคนิคทั้งหมดหมุนรอบการเรนเดอร์ฉากสองครั้ง แต่ละครั้งจากมุมมองที่แตกต่างกัน:
- รอบที่ 1: Depth Pass (มุมมองของแสง) ขั้นแรก เราจะเรนเดอร์ฉากทั้งหมดจากตำแหน่งและการวางแนวที่แน่นอนของแหล่งกำเนิดแสง อย่างไรก็ตาม เราไม่สนใจสีหรือพื้นผิวในรอบนี้ ข้อมูลเดียวที่เราต้องการคือความลึก สำหรับทุกวัตถุที่เรนเดอร์ เราจะบันทึกระยะห่างจากแหล่งกำเนิดแสง ชุดของค่าความลึกนี้จะถูกเก็บไว้ในพื้นผิวพิเศษที่เรียกว่า shadow map หรือ depth map แต่ละพิกเซลในแผนที่นี้แสดงถึงระยะทางไปยังวัตถุที่ใกล้ที่สุดจากมุมมองของแสงในทิศทางที่ระบุ
- รอบที่ 2: Scene Pass (มุมมองของกล้อง) จากนั้น เราจะเรนเดอร์ฉากตามปกติจากมุมมองของกล้องหลัก แต่สำหรับทุกพิกเซลที่วาด เราจะทำการคำนวณเพิ่มเติม เรากำหนดตำแหน่งของพิกเซลนั้นในพื้นที่ 3D แล้วถามว่า "จุดนี้อยู่ห่างจากแหล่งกำเนิดแสงเท่าใด" จากนั้นเราจะเปรียบเทียบระยะทางนี้กับค่าที่เก็บไว้ใน shadow map ของเรา (จากรอบที่ 1) ที่ตำแหน่งที่สอดคล้องกัน
ตรรกะนั้นง่าย:
- ถ้าระยะทางปัจจุบันของพิกเซลจากแสง มากกว่า ระยะทางที่เก็บไว้ใน shadow map แสดงว่ามีวัตถุอื่นอยู่ใกล้กับแสงมากกว่าตามแนวสายตาเดียวกัน ดังนั้น พิกเซลปัจจุบันจึงอยู่ในเงา
- ถ้าระยะทางของพิกเซล น้อยกว่าหรือเท่ากับ ระยะทางใน shadow map แสดงว่าไม่มีสิ่งใดกีดขวาง และพิกเซลจะสว่างเต็มที่
การตั้งค่าฉาก
ในการใช้ shadow mapping ใน WebGL คุณต้องมีส่วนประกอบหลักหลายอย่าง:
- แหล่งกำเนิดแสง: นี่อาจเป็นแสงทิศทาง (เช่น ดวงอาทิตย์) แสงจุด (เช่น หลอดไฟ) หรือสปอตไลต์ ประเภทของแสงจะเป็นตัวกำหนดชนิดของเมทริกซ์การฉายภาพที่ใช้ระหว่าง depth pass
- Framebuffer Object (FBO): โดยปกติแล้ว WebGL จะเรนเดอร์ไปยัง framebuffer เริ่มต้นของหน้าจอ ในการสร้าง shadow map ของเรา เราต้องมีเป้าหมายการเรนเดอร์นอกหน้าจอ FBO ช่วยให้เราสามารถเรนเดอร์ลงในพื้นผิวแทนที่จะเป็นหน้าจอ FBO ของเราจะได้รับการกำหนดค่าด้วยสิ่งที่แนบมากับพื้นผิวความลึก
- Shader สองชุด: คุณจะต้องมีโปรแกรม shader หนึ่งโปรแกรมสำหรับ depth pass (โปรแกรมที่ง่ายมาก) และอีกโปรแกรมหนึ่งสำหรับ scene pass สุดท้าย (ซึ่งจะมีตรรกะการคำนวณเงา)
- Matrices: คุณจะต้องใช้เมทริกซ์ model, view และ projection มาตรฐานสำหรับกล้อง ที่สำคัญ คุณจะต้องมีเมทริกซ์ view และ projection สำหรับแหล่งกำเนิดแสงด้วย ซึ่งมักจะรวมกันเป็น "light space matrix" เดียว
บทที่ 2: ไปป์ไลน์การเรนเดอร์สองรอบโดยละเอียด
มาแบ่งการเรนเดอร์สองรอบทีละขั้นตอน โดยเน้นที่บทบาทของเมทริกซ์และ shaders
รอบที่ 1: Depth Pass (จากมุมมองของแสง)
เป้าหมายของรอบนี้คือการใส่ข้อมูลลงในพื้นผิวความลึกของเรา นี่คือวิธีการทำงาน:
- Bind the FBO: ก่อนที่จะวาด ให้สั่งให้ WebGL เรนเดอร์ไปยัง FBO ที่กำหนดเองของคุณแทนที่จะเป็น canvas
- Configure the Viewport: ตั้งค่าขนาด viewport ให้ตรงกับขนาดของพื้นผิว shadow map ของคุณ (เช่น 1024x1024 พิกเซล)
- Clear the Depth Buffer: ตรวจสอบให้แน่ใจว่าบัฟเฟอร์ความลึกของ FBO ถูกล้างก่อนที่จะเรนเดอร์
- Create the Light's Matrices:
- Light View Matrix: เมทริกซ์นี้แปลงโลกให้เป็นมุมมองของแสง สำหรับแสงทิศทาง โดยทั่วไปแล้วจะสร้างด้วยฟังก์ชัน `lookAt` โดยที่ "eye" คือตำแหน่งของแสงและ "target" คือทิศทางที่แสงชี้ไป
- Light Projection Matrix: สำหรับแสงทิศทางซึ่งมีรังสีขนาน จะใช้ orthographic projection สำหรับแสงจุดหรือสปอตไลต์ จะใช้ perspective projection เมทริกซ์นี้กำหนดปริมาตรในพื้นที่ (กล่องหรือ frustum) ที่จะสร้างเงา
- Use the Depth Shader Program: นี่คือ shader ที่เรียบง่าย หน้าที่เดียวของ vertex shader คือการคูณตำแหน่ง vertex ด้วยเมทริกซ์ view และ projection ของแสง fragment shader นั้นง่ายยิ่งกว่า: มันเพียงแค่เขียนค่าความลึกของ fragment (พิกัด z) ลงในพื้นผิวความลึก ใน WebGL สมัยใหม่ คุณไม่จำเป็นต้องมี fragment shader ที่กำหนดเองด้วยซ้ำ เนื่องจาก FBO สามารถกำหนดค่าให้จับภาพบัฟเฟอร์ความลึกได้โดยอัตโนมัติ
- Render the Scene: วาดวัตถุที่สร้างเงาทั้งหมดในฉากของคุณ ตอนนี้ FBO มี shadow map ที่เสร็จสมบูรณ์ของเราแล้ว
รอบที่ 2: Scene Pass (จากมุมมองของกล้อง)
ตอนนี้เราจะเรนเดอร์ภาพสุดท้าย โดยใช้ shadow map ที่เราเพิ่งสร้างขึ้นเพื่อกำหนดเงา
- Unbind the FBO: สลับกลับไปเรนเดอร์ไปยัง framebuffer canvas เริ่มต้น
- Configure the Viewport: ตั้งค่า viewport กลับเป็นขนาด canvas
- Clear the Screen: ล้างบัฟเฟอร์สีและความลึกของ canvas
- Use the Scene Shader Program: นี่คือที่ที่เวทมนตร์เกิดขึ้น shader นี้มีความซับซ้อนมากขึ้น
- Vertex Shader: shader นี้ต้องทำสองสิ่ง ประการแรก มันจะคำนวณตำแหน่ง vertex สุดท้ายโดยใช้เมทริกซ์ model, view และ projection ของกล้องตามปกติ ประการที่สอง มันต้องคำนวณตำแหน่งของ vertex จากมุมมองของแสงโดยใช้ light space matrix จากรอบที่ 1 ด้วย พิกัดที่สองนี้จะถูกส่งไปยัง fragment shader เป็น varying
- Fragment Shader: นี่คือหัวใจหลักของตรรกะเงา สำหรับแต่ละ fragment:
- รับตำแหน่งที่แก้ไขแล้วใน light space จาก vertex shader
- ทำการ perspective divide บนพิกัดนี้ (หาร x, y, z ด้วย w) สิ่งนี้จะแปลงเป็น Normalized Device Coordinates (NDC) โดยมีช่วงตั้งแต่ -1 ถึง 1
- แปลง NDC เป็นพิกัดพื้นผิว (ซึ่งมีช่วงตั้งแต่ 0 ถึง 1) เพื่อให้เราสามารถสุ่มตัวอย่าง shadow map ของเราได้ นี่คือการดำเนินการ scale และ bias ที่ง่าย: `texCoord = ndc * 0.5 + 0.5;`
- ใช้พิกัดพื้นผิวเหล่านี้เพื่อสุ่มตัวอย่างพื้นผิว shadow map ที่สร้างขึ้นในรอบที่ 1 สิ่งนี้จะทำให้เรา `depthFromShadowMap`
- ความลึกปัจจุบันของ fragment จากมุมมองของแสงคือองค์ประกอบ z จากพิกัด light space ที่แปลงแล้ว เราจะเรียกมันว่า `currentDepth`
- เปรียบเทียบความลึก: หาก `currentDepth > depthFromShadowMap` fragment จะอยู่ในเงา เราจะต้องเพิ่ม bias เล็กน้อยในการตรวจสอบนี้เพื่อหลีกเลี่ยง artifact ที่เรียกว่า "shadow acne" ซึ่งเราจะกล่าวถึงต่อไป
- จากการเปรียบเทียบ ให้กำหนด shadow factor (เช่น 1.0 สำหรับสว่าง, 0.3 สำหรับมีเงา)
- ใช้ shadow factor นี้กับการคำนวณสีสุดท้าย (เช่น คูณส่วนประกอบแสง ambient และ diffuse ด้วย shadow factor)
- Render the Scene: วาดวัตถุทั้งหมดในฉาก
บทที่ 3: ปัญหาและวิธีแก้ไขทั่วไป
การใช้ shadow mapping พื้นฐานจะเผยให้เห็น visual artifacts ทั่วไปหลายอย่าง การทำความเข้าใจและแก้ไข artifacts เหล่านั้นเป็นสิ่งสำคัญเพื่อให้ได้ผลลัพธ์คุณภาพสูง
Shadow Acne (Self-Shadowing Artifacts)
ปัญหา: คุณอาจเห็นรูปแบบที่แปลกและไม่ถูกต้องของเส้นสีเข้มหรือรูปแบบคล้าย Moiré บนพื้นผิวที่ควรสว่างเต็มที่ สิ่งนี้เรียกว่า "shadow acne" เกิดขึ้นเนื่องจากค่าความลึกที่เก็บไว้ใน shadow map และค่าความลึกที่คำนวณระหว่าง scene pass มีไว้สำหรับ พื้นผิวเดียวกัน เนื่องจากความไม่ถูกต้องของ floating-point และความละเอียดที่จำกัดของ shadow map ข้อผิดพลาดเล็กน้อยอาจทำให้ fragment กำหนดอย่างไม่ถูกต้องว่าอยู่ข้างหลังตัวเอง ส่งผลให้เกิด self-shadowing
วิธีแก้ไข: Depth Bias วิธีแก้ปัญหาที่ง่ายที่สุดคือการใส่ bias เล็กน้อยใน `currentDepth` ก่อนการเปรียบเทียบ การทำให้ fragment ดูเหมือนอยู่ใกล้กับแสงมากกว่าที่เป็นจริงเล็กน้อย เราจะผลักมัน "ออก" จากเงาของมันเอง
float shadow = currentDepth > depthFromShadowMap + bias ? 0.3 : 1.0;
การค้นหาค่า bias ที่เหมาะสมคือการรักษาสมดุลที่ละเอียดอ่อน หากเล็กเกินไป acne จะยังคงอยู่ หากใหญ่เกินไป คุณจะเจอปัญหาต่อไป
Peter Panning
ปัญหา: Artifact นี้ตั้งชื่อตามตัวละครที่สามารถบินได้และทำเงาหายไป ปรากฏเป็นช่องว่างที่มองเห็นได้ระหว่างวัตถุและเงา ทำให้วัตถุดูเหมือนลอยหรือขาดการเชื่อมต่อจากพื้นผิวที่ควรวางอยู่ เป็นผลโดยตรงจากการใช้ depth bias ที่ใหญ่เกินไป
วิธีแก้ไข: Slope-Scale Depth Bias วิธีแก้ปัญหาที่แข็งแกร่งกว่า bias คงที่คือการทำให้ bias ขึ้นอยู่กับความชันของพื้นผิวเมื่อเทียบกับแสง รูปหลายเหลี่ยมที่ชันกว่ามีแนวโน้มที่จะเกิด acne มากกว่าและต้องการ bias ที่ใหญ่กว่า รูปหลายเหลี่ยมที่แบนกว่าต้องการ bias ที่เล็กกว่า API กราฟิกส่วนใหญ่ รวมถึง WebGL มีฟังก์ชันในการใช้ bias ประเภทนี้โดยอัตโนมัติระหว่าง depth pass ซึ่งโดยทั่วไปแล้วจะดีกว่า bias แบบแมนนวลใน fragment shader
Perspective Aliasing (Jagged Edges)
ปัญหา: ขอบของเงาของคุณดูเป็นบล็อก ขรุขระ และเป็นพิกเซล นี่คือรูปแบบหนึ่งของ aliasing เกิดขึ้นเนื่องจากความละเอียดของ shadow map มีจำกัด พิกเซลเดียว (หรือ texel) ใน shadow map อาจครอบคลุมพื้นที่ขนาดใหญ่บนพื้นผิวในฉากสุดท้าย โดยเฉพาะอย่างยิ่งสำหรับพื้นผิวที่อยู่ใกล้กล้องหรือพื้นผิวที่มองจากมุมเฉียง ความไม่ตรงกันในความละเอียดนี้ทำให้เกิดลักษณะที่เป็นบล็อก
วิธีแก้ไข: การเพิ่มความละเอียดของ shadow map (เช่น จาก 1024x1024 เป็น 4096x4096) สามารถช่วยได้ แต่มันมาพร้อมกับค่าใช้จ่ายด้านหน่วยความจำและประสิทธิภาพที่สำคัญ และไม่ได้แก้ปัญหาพื้นฐานอย่างสมบูรณ์ วิธีแก้ปัญหาที่แท้จริงอยู่ที่เทคนิคขั้นสูงกว่า
บทที่ 4: เทคนิค Shadow Mapping ขั้นสูง
Shadow mapping พื้นฐานให้รากฐาน แต่แอปพลิเคชันระดับมืออาชีพใช้อัลกอริทึมที่ซับซ้อนกว่าเพื่อเอาชนะข้อจำกัด โดยเฉพาะอย่างยิ่ง aliasing
Percentage-Closer Filtering (PCF)
PCF เป็นเทคนิคที่ใช้กันมากที่สุดในการทำให้ขอบเงาอ่อนลงและลด aliasing แทนที่จะใช้ตัวอย่างเดียวจาก shadow map และทำการตัดสินใจแบบไบนารี (อยู่ในเงาหรือไม่) PCF จะใช้ตัวอย่างหลายตัวอย่างจากพื้นที่รอบๆ พิกัดเป้าหมาย
แนวคิด: สำหรับแต่ละ fragment เราจะสุ่มตัวอย่าง shadow map ไม่ใช่แค่ครั้งเดียว แต่ในรูปแบบกริด (เช่น 3x3 หรือ 5x5) รอบๆ พิกัดพื้นผิวที่ฉายของ fragment สำหรับแต่ละตัวอย่างเหล่านี้ เราจะทำการเปรียบเทียบความลึก ค่าเงาสุดท้ายคือ ค่าเฉลี่ย ของการเปรียบเทียบทั้งหมดเหล่านี้ ตัวอย่างเช่น หาก 4 ใน 9 ตัวอย่างอยู่ในเงา fragment จะถูกเงา 4/9 ส่งผลให้เกิด penumbra ที่ราบรื่น (ขอบเงาที่นุ่มนวล)
การใช้งาน: สิ่งนี้ทำทั้งหมดภายใน fragment shader เกี่ยวข้องกับลูปที่วนซ้ำเหนือเคอร์เนลขนาดเล็ก สุ่มตัวอย่าง shadow map ที่แต่ละออฟเซ็ต และสะสมผลลัพธ์ WebGL 2 มีการสนับสนุนฮาร์ดแวร์ (`texture` ด้วย `sampler2DShadow`) ที่สามารถทำการเปรียบเทียบและการกรองได้อย่างมีประสิทธิภาพมากขึ้น
ประโยชน์: ปรับปรุงคุณภาพเงาอย่างมากโดยการแทนที่ขอบที่แข็งและมี aliasing ด้วยขอบที่นุ่มนวล
ค่าใช้จ่าย: ประสิทธิภาพลดลงตามจำนวนตัวอย่างที่ใช้ต่อ fragment
Cascaded Shadow Maps (CSM)
CSM เป็นโซลูชันมาตรฐานอุตสาหกรรมสำหรับการเรนเดอร์เงาจากแหล่งกำเนิดแสงทิศทางเดียว (เช่น ดวงอาทิตย์) เหนือฉากขนาดใหญ่มาก มันจัดการกับปัญหา perspective aliasing โดยตรง
แนวคิด: แนวคิดหลักคือวัตถุที่อยู่ใกล้กล้องต้องการความละเอียดของเงาที่สูงกว่าวัตถุที่อยู่ไกลออกไปมาก CSM แบ่ง view frustum ของกล้องออกเป็นหลายส่วน หรือ "cascades" ตามความลึก แผนที่เงาคุณภาพสูงแยกต่างหากจะถูกเรนเดอร์สำหรับแต่ละ cascade cascade ที่ใกล้กับกล้องมากที่สุดครอบคลุมพื้นที่ขนาดเล็กของ world space ดังนั้นจึงมีความละเอียดที่มีประสิทธิภาพสูงมาก Cascades ที่อยู่ไกลออกไปครอบคลุมพื้นที่ที่ใหญ่ขึ้นเรื่อยๆ ด้วยขนาดพื้นผิวเดียวกัน ซึ่งเป็นที่ยอมรับได้เนื่องจากรายละเอียดเหล่านั้นมองเห็นได้น้อยกว่าสำหรับผู้เล่น
การใช้งาน: สิ่งนี้ซับซ้อนกว่ามาก
- ใน CPU ให้แบ่ง frustum ของกล้องออกเป็น 2-4 cascades
- สำหรับแต่ละ cascade ให้คำนวณเมทริกซ์การฉายภาพ orthographic ที่กระชับพอดีสำหรับแสงที่ล้อมรอบส่วนนั้นของ frustum อย่างสมบูรณ์แบบ
- ใน rendering loop ให้ทำการ depth pass หลายครั้ง—หนึ่งครั้งสำหรับแต่ละ cascade เรนเดอร์ไปยัง shadow map ที่แตกต่างกัน (หรือภูมิภาคของ texture atlas)
- ใน fragment shader scene pass สุดท้าย ให้กำหนดว่า cascade ใดที่ fragment ปัจจุบันเป็นของโดยอิงตามระยะห่างจากกล้อง
- สุ่มตัวอย่าง shadow map ของ cascade ที่เหมาะสมเพื่อคำนวณเงา
ประโยชน์: ให้เงาที่มีความละเอียดสูงอย่างสม่ำเสมอในระยะทางที่กว้าง ทำให้เหมาะสำหรับสภาพแวดล้อมกลางแจ้ง
Variance Shadow Maps (VSM)
VSM เป็นอีกเทคนิคหนึ่งสำหรับการสร้างเงาที่นุ่มนวล แต่ใช้แนวทางที่แตกต่างจาก PCF
แนวคิด: แทนที่จะเก็บเฉพาะความลึกใน shadow map VSM จะเก็บสองค่า: ความลึก (moment แรก) และความลึกยกกำลังสอง (moment ที่สอง) สองค่านี้ช่วยให้เราคำนวณความแปรปรวนของการกระจายความลึกได้ การใช้เครื่องมือทางคณิตศาสตร์ที่เรียกว่า Chebyshev's inequality เราสามารถประเมินความน่าจะเป็นที่ fragment จะอยู่ในเงา ข้อได้เปรียบที่สำคัญคือพื้นผิว VSM สามารถเบลอโดยใช้การกรองเชิงเส้นและ mipmapping ที่เร่งด้วยฮาร์ดแวร์มาตรฐาน ซึ่งเป็นสิ่งที่ไม่ถูกต้องทางคณิตศาสตร์สำหรับ shadow map มาตรฐาน สิ่งนี้ช่วยให้เกิดเงา penumbras ขนาดใหญ่ นุ่มนวล และราบรื่นมากด้วยค่าใช้จ่ายด้านประสิทธิภาพคงที่
ข้อเสีย: จุดอ่อนหลักของ VSM คือ "light bleeding" ที่แสงอาจปรากฏว่าทะลุผ่านวัตถุในสถานการณ์ที่มี occluders ที่ทับซ้อนกัน เนื่องจากค่าประมาณทางสถิติอาจพังทลายลง
บทที่ 5: เคล็ดลับการใช้งานจริงและประสิทธิภาพ
การเลือกความละเอียด Shadow Map ของคุณ
ความละเอียดของ shadow map ของคุณคือข้อแลกเปลี่ยนโดยตรงระหว่างคุณภาพและประสิทธิภาพ พื้นผิวที่ใหญ่ขึ้นให้เงาที่คมชัดขึ้น แต่ใช้หน่วยความจำวิดีโอมากขึ้นและใช้เวลานานกว่าในการเรนเดอร์และสุ่มตัวอย่าง ขนาดทั่วไป ได้แก่:
- 1024x1024: พื้นฐานที่ดีสำหรับแอปพลิเคชันจำนวนมาก
- 2048x2048: ให้การปรับปรุงคุณภาพที่เห็นได้ชัดเจนสำหรับแอปพลิเคชันเดสก์ท็อป
- 4096x4096: คุณภาพสูง มักใช้สำหรับ hero assets หรือในเอนจินที่มี culling ที่แข็งแกร่ง
การปรับ Frustum ของแสงให้เหมาะสม
เพื่อให้ได้รับประโยชน์สูงสุดจากทุกพิกเซลใน shadow map ของคุณ เป็นสิ่งสำคัญอย่างยิ่งที่ปริมาตรการฉายภาพของแสง (กล่อง orthographic หรือ perspective frustum) จะต้องพอดีกับองค์ประกอบฉากที่ต้องการเงาให้ได้มากที่สุด สำหรับแสงทิศทาง หมายถึงการปรับการฉายภาพ orthographic ให้ครอบคลุมเฉพาะส่วนที่มองเห็นได้ของ frustum ของกล้องเท่านั้น พื้นที่ที่สูญเปล่าใน shadow map คือความละเอียดที่สูญเปล่า
WebGL Extensions and Versions
WebGL 1 vs. WebGL 2: แม้ว่า shadow mapping จะเป็นไปได้ใน WebGL 1 แต่ก็ง่ายและมีประสิทธิภาพมากกว่ามากใน WebGL 2 WebGL 1 ต้องการส่วนขยาย `WEBGL_depth_texture` เพื่อสร้างพื้นผิวความลึก WebGL 2 มีฟังก์ชันนี้ในตัว นอกจากนี้ WebGL 2 ยังให้การเข้าถึง shadow samplers (`sampler2DShadow`) ซึ่งสามารถทำการ PCF ที่เร่งด้วยฮาร์ดแวร์ ทำให้ประสิทธิภาพเพิ่มขึ้นอย่างมากเมื่อเทียบกับลูป PCF แบบแมนนวลใน shader
การแก้ไขจุดบกพร่องเงา
เงาอาจเป็นเรื่องยากที่จะแก้ไขจุดบกพร่อง เทคนิคที่มีประโยชน์ที่สุดคือการ แสดงภาพ shadow map แก้ไขแอปพลิเคชันของคุณชั่วคราวเพื่อเรนเดอร์พื้นผิวความลึกจากแหล่งกำเนิดแสงเฉพาะโดยตรงบน quad บนหน้าจอ สิ่งนี้ช่วยให้คุณเห็นได้อย่างแม่นยำว่าแสง "เห็น" อะไร สิ่งนี้สามารถเปิดเผยปัญหาเกี่ยวกับเมทริกซ์ของแสง Frustum culling หรือการเรนเดอร์วัตถุระหว่าง depth pass ได้ทันที
สรุป
Real-time shadow mapping เป็นรากฐานสำคัญของกราฟิก 3D สมัยใหม่ เปลี่ยนฉากที่แบนราบและไร้ชีวิตชีวาให้เป็นโลกที่น่าเชื่อและมีไดนามิก แม้ว่าแนวคิดของการเรนเดอร์จากมุมมองของแสงจะง่าย แต่การได้ผลลัพธ์คุณภาพสูงและปราศจาก artifacts จำเป็นต้องมีความเข้าใจอย่างลึกซึ้งเกี่ยวกับกลไกพื้นฐาน ตั้งแต่ไปป์ไลน์สองรอบไปจนถึงความแตกต่างของ depth bias และ aliasing
ด้วยการเริ่มต้นด้วยการใช้งานขั้นพื้นฐาน คุณสามารถจัดการกับ artifacts ทั่วไป เช่น shadow acne และ jagged edges ได้อย่างต่อเนื่อง จากนั้น คุณสามารถยกระดับภาพของคุณด้วยเทคนิคขั้นสูง เช่น PCF สำหรับเงาที่นุ่มนวล หรือ Cascaded Shadow Maps สำหรับสภาพแวดล้อมขนาดใหญ่ การเดินทางสู่การเรนเดอร์เงาเป็นตัวอย่างที่สมบูรณ์แบบของการผสมผสานระหว่างศิลปะและวิทยาศาสตร์ที่ทำให้กราฟิกคอมพิวเตอร์น่าสนใจมาก เราขอแนะนำให้คุณทดลองใช้เทคนิคเหล่านี้ ผลักดันขอบเขตของมัน และนำความสมจริงระดับใหม่มาสู่โปรเจ็กต์ WebGL ของคุณ